package GameLogic

import (
	"GameLogic/ECMap"
	"PBMessage"
	"Player"
	"math/rand"
	"time"

	"git.oschina.net/yangdao/GameLib/Unit"
	"git.oschina.net/yangdao/goerl"

	"github.com/golang/protobuf/proto"

	"git.oschina.net/yangdao/extlib/log"
)

//GameScene 游戏逻辑场景
type GameScene struct {
	players  map[int64]*ScenePlayer
	allUnits map[int64]*GameUnit

	changedUnits map[int64]*unitPropertySyncTuple
	tickPackage  *PBMessage.SC_GameSceneTick

	gameMap *ECMap.GameMap
}

type unitPropertySyncTuple struct {
	Data  *PBMessage.SC_SyncUnitProperty
	IsUse bool
}

//NewGameScene 创建游戏逻辑场景
func NewGameScene() *GameScene {
	log.Log("GameLogic,Info", "创建游戏场景啦")
	return &GameScene{
		players:      make(map[int64]*ScenePlayer),
		allUnits:     make(map[int64]*GameUnit),
		changedUnits: make(map[int64]*unitPropertySyncTuple),
		tickPackage:  &PBMessage.SC_GameSceneTick{},
	}
}
func (this *GameScene) AddGameUnit(unit *GameUnit) {
	this.allUnits[unit.Unit.GUID] = unit
	unit.Unit.AbilityExecEvent = this.OnAbilityExec
}
func (this *GameScene) RemoveUnit(id int64) {
	optUnit := &PBMessage.SC_OptUnit{}
	optUnit.OptType = proto.Int32(0)
	optUnit.UnitId = proto.Int64(this.allUnits[id].Unit.GUID)
	this.sendEachPlayer(PBMessage.PBID_SC_OptUnit, optUnit)
	delete(this.allUnits, id)
}

//Load 初始化加载游戏场景
func (this *GameScene) Load() {
	randMapIndex := rand.Intn(len(ECMap.ECMaps))
	this.gameMap = ECMap.ECMaps[randMapIndex].Clone()
	this.gameMap.OnLoad()
}

func (this *GameScene) genMapPackageData() *PBMessage.SC_EnterGameRoom {
	mapItemPkg := &PBMessage.SC_EnterGameRoom{}
	mapItemPkg.GameMapId = proto.Int32(0)
	itemLen := this.gameMap.Items.Count()
	mapItemPkg.ItemId = make([]int32, itemLen)
	mapItemPkg.PosX = make([]int32, itemLen)
	mapItemPkg.PosZ = make([]int32, itemLen)
	mapItemPkg.ShapeType = make([]int32, itemLen)
	mapItemPkg.ShapeArg0 = make([]int32, itemLen)
	mapItemPkg.ShapeArg1 = make([]int32, itemLen)
	for i := 0; i < itemLen; i++ {
		curItem := this.gameMap.Items.Nth(i).(*ECMap.MapItem)
		mapItemPkg.ItemId[i] = int32(curItem.Slot)
		PosX, PosZ := curItem.Shape.Center()
		mapItemPkg.PosX[i] = int32(PosX)
		mapItemPkg.PosZ[i] = int32(PosZ)
		mapItemPkg.ShapeType[i] = int32(PosZ * 100)
		mapItemPkg.ShapeArg0[i] = int32(PosZ * 100)
		mapItemPkg.ShapeArg1[i] = int32(PosZ * 100)
	}
	return mapItemPkg
}

func (this *GameScene) EachUnit(fn func(unit Unit.IUnit)) {
	for _, unit := range this.allUnits {
		fn(unit)
	}
}

//OnAddPlayer 添加一个新玩家进入场景
func (this *GameScene) OnAddPlayer(player *Player.GamePlayer) {
	newScenePlayer := &ScenePlayer{
		player:   player,
		GameUnit: &GameUnit{Unit: &Unit.Unit{Scene: this}},
	}
	newScenePlayer.GameUnit.scenePlayer = newScenePlayer
	newScenePlayer.GameUnit.Init(player.GUID, true, this)
	this.players[player.GUID] = newScenePlayer
	this.AddGameUnit(newScenePlayer.GameUnit)

	goerl.SendByName("NetNode", []interface{}{":ToClientMail", player.SessID, int(PBMessage.PBID_SC_EnterGameRoom), this.genMapPackageData()})
}

//OnRemovePlayer 移除一个游戏玩家
func (this *GameScene) OnRemovePlayer(guid int64) {

	log.Log("GameLogic,Info", "游戏逻辑场景移除一个玩家"+this.players[guid].player.DevciveID)
	delete(this.players, guid)
	delete(this.allUnits, guid)

	delete(this.players, guid+1)
	delete(this.allUnits, guid+1)
}

//OnSelectUnit 选择单位ID
func (gameScene *GameScene) OnSelectUnit(unitID int32, guid int64) {
	scenePlayer := gameScene.players[guid]
	scenePlayer.GameUnit.PushCommond(CMD_SELECTUNIT, unitID)
	log.Log("GameLogic,Info", gameScene.players[guid].player.DevciveID+"选择了角色")
	gameScene.syncCreateUnit(scenePlayer.GameUnit)
	for _, curPlayer := range gameScene.players {
		if curPlayer == scenePlayer {
			continue
		}
		pkg := curPlayer.GameUnit.toSCCreateUnit()
		goerl.SendByName("NetNode", []interface{}{":ToClientMail", scenePlayer.player.SessID, int(PBMessage.PBID_SC_CreateUnit), pkg})
	}
	scenePlayer.GameUnit.Unit.PropertyChangeEvent = gameScene.OnUnitProperty
	scenePlayer.GameUnit.Unit.UnitDieEvent = gameScene.OnUnitDie
	scenePlayer.GameUnit.Unit.BehaviorEvent = gameScene.OnBehaviorEvent

	//临时拷贝一个测试player
	/*
		copyUnit := &GameUnit{Unit: &Unit.Unit{GUID: scenePlayer.player.GUID + 1}}
		copyUnit.Init(scenePlayer.player.GUID+1, true, gameScene)
		copyUnit.Load()
		copyUnit.Name = "拷贝滑稽"
		gameScene.allUnits[scenePlayer.player.GUID+1] = copyUnit
		scCreateUnit := copyUnit.toSCCreateUnit()
		for _, curUnit := range gameScene.players {
			goerl.SendByName("NetNode", []interface{}{":ToClientMail", curUnit.player.SessID, int(PBMessage.PBID_SC_CreateUnit), scCreateUnit})
		}
		copyUnit.Unit.PropertyChangeEvent = gameScene.OnUnitProperty
		copyUnit.Unit.UnitDieEvent = gameScene.OnUnitDie*/
}

func (gameScene *GameScene) OnRunAbility(guid int64, abilityName string) {
	if player, ok := gameScene.players[guid]; ok {
		player.GameUnit.PushCommond(CMD_RUNABILITY, abilityName)
	}
}

func (gameScene *GameScene) syncCreateUnit(unit *GameUnit) {
	scCreateUnit := unit.toSCCreateUnit()
	for _, curUnit := range gameScene.players {
		goerl.SendByName("NetNode", []interface{}{":ToClientMail", curUnit.player.SessID, int(PBMessage.PBID_SC_CreateUnit), scCreateUnit})
	}
}

func (gameScene *GameScene) sendEachPlayer(msgID int, msg proto.Message) {
	for _, curUnit := range gameScene.players {
		goerl.SendByName("NetNode", []interface{}{":ToClientMail", curUnit.player.SessID, msgID, msg})
	}
}

//OnUpdate 更新
func (this *GameScene) OnUpdate(dt int) {
	for _, u := range this.allUnits {
		if u.UnitID >= 0 {
			u.onUpdate(dt)
		}
	}
	curTime := time.Now().UnixNano() / 1e6
	this.tickPackage.Time = proto.Int64(curTime)
	this.sendEachPlayer(PBMessage.PBID_SC_GameSceneTick, this.tickPackage)
	this.syncGameUnitProperty(curTime)
}

func (this *GameScene) OnUnitProperty(unit *Unit.Unit, key string, oldV interface{}, newV interface{}) {
	if key == ":MoveSpeed" {
		curUnit := this.allUnits[unit.GUID]
		if curUnit.curStateName == "canRunAbility" || curUnit.curStateName == "run" {
			this.allUnits[unit.GUID].PushCommond(CMD_SPEEDCHANGE)
		} else {
			curUnit.Speed = curUnit.GetProperty(":MoveSpeed").(int)
		}
	}
	this.setChangeUnitProperty(unit, key, oldV, newV)
}

func (this *GameScene) setChangeUnitProperty(unit *Unit.Unit, key string, oldV interface{}, newV interface{}) {
	if pkg, ok := this.changedUnits[unit.GUID]; ok {
		this.setSyncUnitPropertyPackage(pkg.Data, key, newV)
		pkg.IsUse = true
	} else {
		genPackage := &PBMessage.SC_SyncUnitProperty{UnitId: proto.Int64(unit.GUID)}
		this.setSyncUnitPropertyPackage(genPackage, key, newV)
		this.changedUnits[unit.GUID] = &unitPropertySyncTuple{Data: genPackage, IsUse: true}
	}
}

func (this *GameScene) setSyncUnitPropertyPackage(data *PBMessage.SC_SyncUnitProperty, key string, V interface{}) {
	switch key {
	case ":HP":
		data.HP = proto.Float32(float32(V.(float64)))
	case ":MoveSpeed":
		data.MoveSpeed = proto.Int32(int32(V.(int)))
	case ":Stamina":
		data.Stamina = proto.Float32(float32(V.(float64)))
	}
}

func (this *GameScene) OnUnitDie(unit *Unit.Unit) {
	this.allUnits[unit.GUID].chageState("die")
}

func (this *GameScene) OnBehaviorEvent(unit *Unit.Unit, behavior *Unit.Behavior, typ int) {
	if behavior.Modification == nil {
		return
	}
	for k, _ := range behavior.Modification.ModficationPropertys.Vals {
		this.OnUnitProperty(unit, k.(string), nil, unit.GetProperty(k.(string)))
	}
}

func (this *GameScene) OnAbilityExec(unit *Unit.Unit, ability *Unit.Ability) {
	execAbility := &PBMessage.SC_ExecAbility{}
	execAbility.UnitId = proto.Int64(unit.GUID)
	execAbility.AbilityName = proto.String(ability.Name)
	this.sendEachPlayer(PBMessage.PBID_SC_ExecAbility, execAbility)
}

//OnMoveUnit 移动单位
func (this *GameScene) OnMoveUnit(guid int64, dirX int32, dirY int32, state int32, moveTime int64) {
	this.players[guid].GameUnit.PushCommond(CMD_MOVE, dirX, dirY, state, moveTime)
	scMove := &PBMessage.SC_MoveUnit{}
	scMove.DX = proto.Int32(dirX)
	scMove.DY = proto.Int32(dirY)
	scMove.PosX = proto.Int32(int32(this.players[guid].GameUnit.PosPtr().X * 100))
	scMove.PosY = proto.Int32(int32(this.players[guid].GameUnit.PosPtr().Z * 100))
	scMove.Time = proto.Int64(moveTime)
	scMove.State = proto.Int32(state)
	scMove.UnitId = proto.Int64(guid)

	//fmt.Println(this.players[guid].GameUnit.Unit)
	scMove.Speed = proto.Int32(int32(this.players[guid].GameUnit.Unit.GetProperty(":MoveSpeed").(int)))
	this.sendEachPlayer(PBMessage.PBID_SC_MoveUnit, scMove)
}

func (this *GameScene) syncGameUnitProperty(time int64) {
	for _, pkg := range this.changedUnits {
		if pkg.IsUse {
			pkg.Data.Time = proto.Int64(time)
			this.sendEachPlayer(PBMessage.PBID_SC_SyncUnitProperty, pkg.Data)
		}
	}

	for _, pkg := range this.changedUnits {
		pkg.IsUse = false
	}
}
